#include "rtp.h"

/**************************************************************************
作者：王若语

日期：2022/4/6

功能描述：判断start code是否为001

参数：char* buf--当前帧数的第一位数

返回值：返回1则成功，0则失败

**************************************************************************/
int Rtp::isStartCode3(char* buf)
{
    if (buf[0] == 0 && buf[1] == 0 && buf[2] == 1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**************************************************************************
作者：王若语

日期：2022/4/6

功能描述：判断start code是否为0001

参数：char* buf--当前帧数的第一位数

返回值：返回1则成功，0则失败

**************************************************************************/
int Rtp::isStartCode4(char* buf)
{
    if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**************************************************************************
作者：王若语

日期：2022/4/6

功能描述：判断下一个start code所在位置，通常是一个很大的数值

参数：char* buf--当前帧数的第一位数
     int len--数据总长减去三

返回值：char* buf--当前帧start code的1所在位置，并以数值显示

**************************************************************************/
char* Rtp::findNextStartCode(char* buf, int len)
{
    int i;
    if (len < 3)
    {
        return NULL;
    }

    for (i = 0; i < len-3; ++i)
    {
        if (isStartCode3(buf) || isStartCode4(buf))
        {
            return buf;
        }
        ++buf;
    }
    if (isStartCode3(buf))
    {
        return buf;
    }
    return NULL;
}

/**************************************************************************
作者：王若语

日期：2022/4/6

功能描述：读取一帧的H264文件数据

参数：int fd--文件辨识字
     char* frame--开辟的用于存放读出数据的空间
     int size--每次读取的大小

返回值：frameSize--该帧的大小

**************************************************************************/
int Rtp::getOneFrameFromH264File(int fd, char* frame, int size)
{
    int rSize, frameSize;//读取的fd
    char* nextStartCode;//下一个start code位

    if (fd < 0)
    {
		printf("getFrameFromH264File  if(fd < 0) \n");
        return fd;
	}
    rSize = read(fd, frame, size);
    if (!isStartCode3(frame) && !isStartCode4(frame))
    {
		printf("getFrameFromH264File  1 return -1; \n");
        return -1;
	}
    
    nextStartCode = findNextStartCode(frame+3, rSize-3);
    if (!nextStartCode)
    {
		printf("getFrameFromH264File  2 return -1; \n");
        return -1;
    }
    else
    {
        frameSize = nextStartCode-frame;
        lseek(fd, frameSize-rSize, SEEK_CUR);
    }
    return frameSize;
}

/**************************************************************************
作者：王若语

日期：2022/4/6

功能描述：RTP包头部定义

参数：定义的RTP包结构体里的参数

**************************************************************************/
void Rtp::rtpHeaderInit(struct RtpPacket* rtpPacket, uint8_t csrcLen, uint8_t extension,
                    uint8_t padding, uint8_t version, uint8_t payloadType, uint8_t marker,
                   uint16_t seq, uint32_t timestamp, uint32_t ssrc)
{
    rtpPacket->rtpHeader.csrcLen = csrcLen;
    rtpPacket->rtpHeader.extension = extension;
    rtpPacket->rtpHeader.padding = padding;
    rtpPacket->rtpHeader.version = version;
    rtpPacket->rtpHeader.payloadType =  payloadType;
    rtpPacket->rtpHeader.marker = marker;
    rtpPacket->rtpHeader.seq = seq;
    rtpPacket->rtpHeader.timestamp = timestamp;
    rtpPacket->rtpHeader.ssrc = ssrc;
}

/**************************************************************************
作者：王若语

日期：2022/4/6

功能描述：发送RTP包

参数：

返回值：ret--成功则返回实际传送出去的字符数, 失败返回－1

**************************************************************************/
int Rtp::rtpSendPacket(int socket, const char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize)
{
    struct sockaddr_in addr;
    int ret;
    //建立UDP连接
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip);
    //转换为网络字节序
    rtpPacket->rtpHeader.seq = htons(rtpPacket->rtpHeader.seq);
    rtpPacket->rtpHeader.timestamp = htonl(rtpPacket->rtpHeader.timestamp);
    rtpPacket->rtpHeader.ssrc = htonl(rtpPacket->rtpHeader.ssrc);
    //传输数据
    ret = sendto(socket, (void*)rtpPacket, dataSize+RTP_HEADER_SIZE, 0,
                    (struct sockaddr*)&addr, sizeof(addr));

    rtpPacket->rtpHeader.seq = ntohs(rtpPacket->rtpHeader.seq);
    rtpPacket->rtpHeader.timestamp = ntohl(rtpPacket->rtpHeader.timestamp);
    rtpPacket->rtpHeader.ssrc = ntohl(rtpPacket->rtpHeader.ssrc);
    return ret;
}

/**************************************************************************
作者：王若语

日期：2022/4/6

功能描述：发送RTP包

参数：

返回值：sendBytes--返回实际传送出去的字节

**************************************************************************/
int Rtp::rtpSendH264Frame(int socket, const char* ip, int16_t port, struct RtpPacket* rtpPacket, uint8_t* frame, uint32_t frameSize)
{
    uint8_t naluHeader; // NALU头
    //NALU header定义
	#define NALU_TYPE 0x1F          //0001 1111
	#define NALU_F         0x80     //1000 0000
	#define NALU_NRI    0x60        //0110 0000
    int sendBytes = 0;
    int ret;
    naluHeader = frame[0];
/*
 NALU长度小于RTP最大包长：单一NALU单元模式,一个NALU单元用一个RTP包发送出去。
 只需要在RTP包头后去掉开始码的NALU数据即可。
  RTP header(12bytes) + NALU header (1byte) + NALU payload
*/
    if (frameSize <= RTP_MAX_PKT_SIZE)
    {
        /*NALU头,因为网络字节顺序NBO 和 主机字节顺序的原因，所以是高位在前
         *   7  6 5  4 3 2 1 0 
         *  +-+-+-+-+-+-+-+
         *  |F|NRI|  Type   |
         *  +-+-+-+-+-+-+-+
         */
        memcpy(rtpPacket->rtppayload, frame, frameSize);
        ret = rtpSendPacket(socket, ip, port, rtpPacket, frameSize);
        if (ret < 0)//发送数据小于0则退出
        {
            return -1;
        }
        rtpPacket->rtpHeader.seq++;
        sendBytes += ret;
        if ((naluHeader & NALU_TYPE) == 7 || (naluHeader & NALU_TYPE) == 8) 
            goto out;
    }
    else 
    {
        /*
		/nalu长度大于最大包长：分片模式，一个NALU分成几个RTP包（FUs模式）。
		要在RTP头12自己后添加FU Indicator 和  FU Header  再添加NALU的数据：
		RTP header (12bytes)+ FU Indicator (1byte)  +  FU header(1 byte) + NALU payload
         *  0                   1                   2
         *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         * | FU Indicator  |   FU Header   |   FU payload   ...  |
         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         */

        /*
         *     FU Indicator
         *    0 1 2 3 4 5 6 7
         *   +-+-+-+-+-+-+-+-+
         *   |F|NRI|  Type   |
         *   +---------------+
         */

        /*
         *      FU Header
         *    0 1 2 3 4 5 6 7
         *   +-+-+-+-+-+-+-+-+
         *   |S|E|R|  Type   |
         *   +---------------+
         */

        int pktNum = frameSize / RTP_MAX_PKT_SIZE;       // 完整的RTP包 个数
        int remainPktSize = frameSize % RTP_MAX_PKT_SIZE; // 剩余不完整包的大小
        int i, pos = 1;

        /* 发送完整的包 */
        for (i = 0; i < pktNum; i++)
        {
			//前面的文章说过NAL头TYPE值28是FU-A分片,把原先nal头的 |F|NRI|赋值到FU Indicator的 |F|NRI|
            rtpPacket->rtppayload[0] = (naluHeader & NALU_NRI) | 28;
			//把原先nal头的type的5bits赋值到 FU Header的type的5bits
            rtpPacket->rtppayload[1] = naluHeader & NALU_TYPE;
            
            if (i == 0) //第一包数据
                rtpPacket->rtppayload[1] |= 0x80; // 0x80: 1000s0000 ,S位为1是start
            else if (remainPktSize == 0 && i == pktNum - 1) //最后一包数据
                rtpPacket->rtppayload[1] |= 0x40; // 0x40:01000000,E位为1是end
			//把NALU数据填充到RTP包数据段
            memcpy(rtpPacket->rtppayload+2, frame+pos, RTP_MAX_PKT_SIZE);
            ret = rtpSendPacket(socket, ip, port, rtpPacket, RTP_MAX_PKT_SIZE+2);
            if(ret < 0)
                return -1;

            rtpPacket->rtpHeader.seq++;
            sendBytes += ret;
            pos += RTP_MAX_PKT_SIZE;
        }

        /* 发送剩余的数据 */
        if (remainPktSize > 0)
        {
            rtpPacket->rtppayload[0] = (naluHeader & NALU_NRI) | 28;
            rtpPacket->rtppayload[1] = naluHeader & NALU_TYPE;
            rtpPacket->rtppayload[1] |= 0x40; //end

            memcpy(rtpPacket->rtppayload+2, frame+pos, remainPktSize+2);
            ret = rtpSendPacket(socket, ip, port, rtpPacket, remainPktSize+2);
            if(ret < 0)
                return -1;

            rtpPacket->rtpHeader.seq++;
            sendBytes += ret;
        }
    }

out:

    return sendBytes;
}



